1 using System;
2 using
System.Collections.Generic;
3 using
UnityEngine;
4 using
System.Collections;
5 using
Random = UnityEngine.Random;
6
7 namespace
ProceduralToolkit.Examples
8 {

9     ///
<summary>
10     ///
A single-mesh particle system with birds-like behaviour
11     ///
</summary>
12     ///
<remarks>
13     ///
http://en.wikipedia.org/wiki/Boids
14     ///
</remarks>
15     
public class BoidController
16     {
17         
[Serializable]
18         
public class Config
19         {
20             
public Vector3 anchor = Vector3.zero;
21             
public float spawnSphere = 10;
22             
public float worldSphere = 15;
23
24             
public int swarmCount = 2000;
25             
public int maxSpeed = 10;
26             
public float interactionRadius = 5;
27             
public float cohesionCoefficient = 1;
28             
public float separationDistance = 3;
29             
public float separationCoefficient = 10;
30             
public float alignmentCoefficient = 5;
31
32             ///
<summary>
33             ///
Number of neighbours participating in calculations
34             ///
</summary>
35             
public int maxBoids = 5;
36             ///
<summary>
37             ///
Percentage of swarm simulated in each frame
38             ///
</summary>
39             
public float simulationPercent = 0.01f;
40
41             
public MeshDraft template;
42         }
43
44         
private Config config;
45         
private List<Boid> boids = new List<Boid>();
46         
private MeshDraft draft;
47         
private Mesh mesh;
48         
private List<Boid> neighbours = new List<Boid>();
49         
private int maxSimulationSteps;
50
51         ///
<summary>
52         ///
Generate new colors and positions for boids
53         ///
</summary>
54         
public Mesh Generate(Config config)
55         {
56             
this.config = config;
57
58             
// Avoid vertex count overflow
59             config.swarmCount = Mathf.Min(
65000/config.template.vertexCount, config.swarmCount);
60             
// Optimization trick: in each frame we simulate only small percent of all boids
61             maxSimulationSteps = Mathf.RoundToInt(config.swarmCount*config.simulationPercent);
62             
int vertexCount = config.swarmCount*config.template.vertexCount;
63
64             draft =
new MeshDraft
65             {
66                 name =
"Boids",
67                 vertices =
new List<Vector3>(vertexCount),
68                 triangles =
new List<int>(vertexCount),
69                 normals =
new List<Vector3>(vertexCount),
70                 uv =
new List<Vector2>(vertexCount),
71                 colors =
new List<Color>(vertexCount)
72             };
73
74             
for (var i = 0; i < config.swarmCount; i++)
75             {
76                 
// Assign random starting values for each boid
77                 
var boid = new Boid
78                 {
79                     position = Random.insideUnitSphere*config.spawnSphere,
80                     rotation = Random.rotation,
81                     velocity = Random.onUnitSphere*config.maxSpeed
82                 };
83                 boids.Add(boid);
84
85                 draft.Add(config.template);
86             }
87
88             mesh = draft.ToMesh();
89             mesh.MarkDynamic();
90             
// Set bounds manually for correct culling
91             mesh.bounds =
new Bounds(Vector3.zero, Vector3.one*config.worldSphere*2);
92             
return mesh;
93         }

94
95         ///
<summary>
96         ///
Run simulation
97         ///
</summary>
98         
public IEnumerator CalculateVelocities()
99         {
100             
int simulationStep = 0;
101
102             
for (int currentIndex = 0; currentIndex < boids.Count; currentIndex++)
103             {
104                 
// Optimization trick: in each frame we simulate only small percent of all boids
105                 simulationStep++;
106                 
if (simulationStep > maxSimulationSteps)
107                 {
108                     simulationStep =
0;
109                     
yield return null;
110                 }
111
112                 
var boid = boids[currentIndex];
113                 
// Search for nearest neighbours
114                 neighbours.Clear();
115                 
for (int i = 0; i < boids.Count; i++)
116                 {
117                     Boid neighbour = boids[i];
118
119                     Vector3 toNeighbour = neighbour.position - boid.position;
120                     
if (toNeighbour.sqrMagnitude < config.interactionRadius)
121                     {
122                         neighbours.Add(neighbour);
123                         
if (neighbours.Count == config.maxBoids)
124                         {
125                             
break;
126                         }
127                     }
128                 }
129
130                 
if (neighbours.Count < 2) continue;
131
132                 boid.velocity = Vector3.zero;
133                 boid.cohesion = Vector3.zero;
134                 boid.separation = Vector3.zero;
135                 boid.alignment = Vector3.zero;
136
137                 
// Calculate boid parameters
138                 
int separationCount = 0;
139                 
for (int i = 0; i < neighbours.Count && i < config.maxBoids; i++)
140                 {
141                     Boid neighbour = neighbours[i];
142
143                     boid.cohesion += neighbour.position;
144                     boid.alignment += neighbour.velocity;
145
146                     Vector3 toNeighbour = neighbour.position - boid.position;
147                     
if (toNeighbour.sqrMagnitude > 0 &&
148                         toNeighbour.sqrMagnitude < config.separationDistance*config.separationDistance)
149                     {
150                         boid.separation += toNeighbour/toNeighbour.sqrMagnitude;
151                         separationCount++;
152                     }
153                 }
154
155                 
// Clamp all parameters to safe values
156                 boid.cohesion /= Mathf.Min(neighbours.Count, config.maxBoids);
157                 boid.cohesion = Vector3.ClampMagnitude(boid.cohesion - boid.position, config.maxSpeed);
158                 boid.cohesion *= config.cohesionCoefficient;
159
160                 
if (separationCount > 0)
161                 {
162                     boid.separation /= separationCount;
163                     boid.separation = Vector3.ClampMagnitude(boid.separation, config.maxSpeed);
164                     boid.separation *= config.separationCoefficient;
165                 }
166
167                 boid.alignment /= Mathf.Min(neighbours.Count, config.maxBoids);
168                 boid.alignment = Vector3.ClampMagnitude(boid.alignment, config.maxSpeed);
169                 boid.alignment *= config.alignmentCoefficient;
170
171                 
// Calculate resulting velocity
172                 Vector3 velocity = boid.cohesion + boid.separation + boid.alignment;
173                 boid.velocity = Vector3.ClampMagnitude(velocity, config.maxSpeed);
174                 
if (boid.velocity == Vector3.zero)
175                 {
176                     
// Prevent boids from stopping
177                     boid.velocity = Random.onUnitSphere*config.maxSpeed;
178                 }
179             }
180         }

181
182         ///
<summary>
183         ///
Apply simulation to mesh
184         ///
</summary>
185         
public void Update()
186         {
187             
for (int i = 0; i < boids.Count; i++)
188             {
189                 
var boid = boids[i];
190                 boid.rotation = Quaternion.FromToRotation(Vector3.up, boid.velocity);
191
192                 
// Contain boids in sphere
193                 Vector3 distanceToAnchor = config.anchor - boid.position;
194                 
if (distanceToAnchor.sqrMagnitude > config.worldSphere*config.worldSphere)
195                 {
196                     boid.velocity += distanceToAnchor/config.worldSphere;
197                     boid.velocity = Vector3.ClampMagnitude(boid.velocity, config.maxSpeed);
198                 }
199
200                 boid.position += boid.velocity*Time.deltaTime;
201                 SetBoidVertices(boid, i);
202             }
203             mesh.SetVertices(draft.vertices);
204             mesh.RecalculateNormals();
205         }
206
207         
private void SetBoidVertices(Boid boid, int boidIndex)
208         {
209             
for (int i = 0; i < config.template.vertexCount; i++)
210             {
211                 
int vertexIndex = boidIndex*config.template.vertexCount + i;
212                 draft.vertices[vertexIndex] = boid.rotation*config.template.vertices[i] + boid.position;
213             }
214         }
215
216         
private class Boid
217         {
218             
public Vector3 position;
219             
public Quaternion rotation;
220             
public Vector3 velocity;
221             
public Vector3 cohesion;
222             
public Vector3 separation;
223             
public Vector3 alignment;
224         }
225     }
226 }


Gõ tìm kiếm nhanh...